<?php

namespace app\common\controller;

use Exception;
use think\facade\Env;

class ApiAlipayLogin
{
  private $appId;
  private $gatewayUrl = 'https://openapi.alipay.com/gateway.do';
  private $charset = 'UTF-8';
  private $signType = 'RSA2';
  private $merchantPrivateKey;
  private $appCertPath;
  private $alipayCertPath;
  private $rootCertPath;

  public function __construct()
  {
    $str =  Env::get('ALIPAY.ALIPAY_PRIVATE_KEY');
    $str  = chunk_split($str, 64, "\n");
    $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n"; //开发者私钥

    $config = [
      'app_id' => Env::get('ALIPAY.ALIPAY_APPID'),
      'merchant_private_key' => $privateKey,
      'app_cert_path' => app()->getRootPath() . 'public/cert/alipay/' . Env::get('ALIPAY.ALIPAY_PAY_APP_PUBLIC_SECRET_CERT_PATH'),
      'alipay_cert_path' =>  app()->getRootPath() . 'public/cert/alipay/' . Env::get('ALIPAY.ALIPAY_PAY_PUBLIC_CERT_PATH'),
      'root_cert_path' => app()->getRootPath() . 'public/cert/alipay/' . Env::get('ALIPAY.ALIPAY_PAY_ROOT_CERT_PATH'),
    ];

    $this->appId = $config['app_id'];
    $this->merchantPrivateKey = $config['merchant_private_key'];
    $this->appCertPath = $config['app_cert_path'];
    $this->alipayCertPath = $config['alipay_cert_path'];
    $this->rootCertPath = $config['root_cert_path'];
  }

  /**
   * 从证书中提取序列号
   * @param $cert
   * @return string
   */
  public function getCertSN($certPath)
  {
    $cert = file_get_contents($certPath);
    $ssl = openssl_x509_parse($cert);
    $SN = md5($this->array2string(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
    return $SN;
  }

  public function getRootCertSN($certPath)
  {
    $cert = file_get_contents($certPath);
    $array = explode("-----END CERTIFICATE-----", $cert);

    $SN = null;
    for ($i = 0; $i < count($array) - 1; $i++) {
      $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
      if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
        $ssl[$i]['serialNumber'] = $this->hex2dec($ssl[$i]['serialNumberHex']);
      }
      if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") {
        if ($SN == null) {
          $SN = md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
        } else {

          $SN = $SN . "_" . md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
        }
      }
    }
    return $SN;
  }

  private function array2string($array)
  {
    $string = [];
    if ($array && is_array($array)) {
      foreach ($array as $key => $value) {
        $string[] = $key . '=' . $value;
      }
    }
    return implode(',', $string);
  }

  /**
   * 0x转高精度数字
   * @param $hex
   * @return int|string
   */
  private function hex2dec($hex)
  {
    $dec = 0;
    $len = strlen($hex);
    for ($i = 1; $i <= $len; $i++) {
      $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
    }
    return $dec;
  }



  // 获取根证书序列号
  private function getRootCertSN1($certPath)
  {
    $certContent = file_get_contents($certPath);
    // $certContent = preg_replace('/\x{FEFF}/u', '', $certContent); // 移除 UTF-8 BOM
    // $certContent = str_replace("\r\n", "\n", $certContent); // 替换为 Unix 换行符 
    $certContent = trim($certContent);
    // if (!preg_match('/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/s', $certContent)) {
    //   throw new Exception("证书格式错误");
    // }

    if (empty($certContent)) {
      throw new Exception("根证书文件内容为空");
    }

    // 分割多个证书块
    $certs = explode('-----END CERTIFICATE-----', $certContent);
    $sn = '';
    $certs = array_filter($certs);
    // foreach ($certs as $index => $cert) {

    //   $cert = trim($cert) . PHP_EOL . '-----END CERTIFICATE-----';
    //   $parsed = openssl_x509_parse($cert);

    //   echo "证书块 {$index}:\n"; 
    //   echo "Issuer: " . ($parsed['issuer']['CN'] ?? '未知') . "\n";
    //   echo "Subject: " .($parsed['subject']['CN'] ?? '未知') . "\n";
    //   echo "Serial: " . strtoupper(bin2hex($parsed['serialNumber'])) . "\n\n";

    // }
    // exit;
    foreach ($certs as $cert) {
      $cert = trim($cert);
      if (empty($cert)) continue;

      $cert = trim($cert) . PHP_EOL . '-----END CERTIFICATE-----';
      // 解析证书
      $parsed = openssl_x509_parse($cert);
      if ($parsed === false) {
        continue;
      }
      // 检查是否为支付宝根证书（通过 Issuer 字段）
      $issuer = $parsed['issuer']['CN'] ?? ''; // 支付宝根证书的 Issuer 为 "Alipay Root CA"
      if (strpos($issuer, 'Alipay Root CA') !== false) {
        // 提取序列号（需转换为大写 HEX）
        $sn = strtoupper(bin2hex($parsed['serialNumber']));
        break;
      }
    }
    if (empty($sn)) {
      throw new Exception("根证书序列号解析失败：未找到 Alipay Root CA");
    }

    return $sn;
  }

  // 生成签名
  private function generateSign($params)
  {
    ksort($params);
    $stringToSign = '';
    foreach ($params as $k => $v) {
      $stringToSign .= "&{$k}={$v}";
    }
    $stringToSign = substr($stringToSign, 1);
    openssl_sign($stringToSign, $sign, $this->merchantPrivateKey, OPENSSL_ALGO_SHA256);
    return base64_encode($sign);
  }

  // 执行请求
  public function execute($method, $bizParams)
  {
    // 公共参数
    $params = [
      'app_id' => $this->appId,
      'method' => $method,
      'charset' => $this->charset,
      'sign_type' => $this->signType,
      'timestamp' => date('Y-m-d H:i:s'),
      'version' => '1.0',
      // 'biz_content' => json_encode($bizParams),
      'grant_type' => 'authorization_code',
      'code' => $bizParams['code']
    ];

    // 证书序列号
    $params['app_cert_sn'] = $this->getCertSN($this->appCertPath);
    $params['alipay_root_cert_sn'] = $this->getRootCertSN($this->rootCertPath);
    // 生成签名
    $params['sign'] = $this->generateSign($params);

    // 发送请求
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->gatewayUrl);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_ENCODING, 'UTF-8');
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
    $response = curl_exec($ch);

    $response = mb_convert_encoding($response, 'UTF-8', 'GBK');
    curl_close($ch);
    // 处理响应
    $result = json_decode($response, true);
    $responseKey = 'alipay_system_oauth_token_response';
    if (!empty($result[$responseKey])) {
      $data = $result[$responseKey];
      // 验证签名（需使用支付宝公钥证书）
      if ($this->verifySign($data, $result['sign'])) {
        return $data;
      } else {
        throw new Exception('签名验证失败');
      }
    } else {
      throw new Exception('签名验证失败');
    }
  }

  // 验证签名
  private function verifySign($data, $sign)
  {
    $publicKey = file_get_contents($this->alipayCertPath);
    $pubKey = openssl_get_publickey($publicKey);
    $stringToSign = json_encode($data, JSON_UNESCAPED_UNICODE);
    $success = openssl_verify($stringToSign, base64_decode($sign), $pubKey, OPENSSL_ALGO_SHA256);
    return $success === 1;
  }

  public function login($authCode = '')
  { 
    // 换取用户信息
    try {
      $result = $this->execute('alipay.system.oauth.token', [
        'grant_type' => 'authorization_code',
        'code' => $authCode
      ]); 
      return [
        'status' => 'success',
        'session_key' => '',
        'openid' => $result['user_id'],
        'unionid' => ''
      ];
    } catch (Exception $e) {
      return [
        'status' => 'error',
        'message' => 'Failed to get session key and openid.',
      ];
    }
  }
}
